package dpss.frontend;

import java.io.IOException;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Properties;

import javax.xml.parsers.ParserConfigurationException;

import org.omg.CORBA.ORB;
import org.omg.CosNaming.NameComponent;
import org.omg.CosNaming.NamingContextExt;
import org.omg.CosNaming.NamingContextExtHelper;
import org.omg.PortableServer.POA;
import org.omg.PortableServer.POAHelper;
import org.xml.sax.SAXException;

import dpss.commons.CustomLogger;
import dpss.commons.UDPMessage;
import dpss.commons.Util;
import dpss_api_corba.DpssApi;
import dpss_api_corba.DpssApiHelper;

/**
 * 
 * The FRONT END serves as a gateway to the client. The client
 * only talks to the Front End via CORBA
 * 
 * @author Shewetaank Indora
 * 
 */
public class Frontend {

	

	private static CustomLogger logger = CustomLogger.getInstance();
	
	private Properties prop;
	private static DatagramSocket socket;
	private InetSocketAddress replicaManagerSocketAddress;
	private HashMap<String, InetSocketAddress> leaderSocketAddresses;
	private DpssApiImpl dpssApiImpl;
	

	/**
	 * Start it's own instance
	 * 
	 * @param args
	 */
	public static void main(String[] args) throws IOException {
		Util.printProgramName("CORBA FRONT-END (the gateway)");
		new Frontend();
	}

	/**
	 * Front end constructor
	 */
	private Frontend() {
		
		logger.logConsole(true);
		
		prop = Util.loadProperties("project.properties");
		
		setReplicaManagerSocketAddress();
		setUDPSocket();
		getLeaderSocketAddressesFromRM();
		setupCORBAService();

	}



	/**
	 * Sets the UDP socket
	 */
	private void setUDPSocket() {
		try {
			socket = new DatagramSocket(Integer.parseInt((String) prop.get("self.port")));
		} catch (NumberFormatException | SocketException e1) {
			logger.out("ERROR - Could not parse UDP port");
			System.exit(0);
		}
	}

	/**
	 * Sets the socket address provided from properties
	 */
	private void setReplicaManagerSocketAddress() {
		try {
			 InetAddress rmIp = InetAddress.getByName( (String) prop.get("replicamanager.ip"));
            Integer rmPort = Integer.parseInt( (String) prop.get("replicamanager.port"));
            replicaManagerSocketAddress = new InetSocketAddress(rmIp, rmPort);
		} catch (UnknownHostException e) {
			logger.out("ERROR - Could not connect to replica manager");
			System.exit(0);
		}
	}

	@SuppressWarnings("unchecked")
	private void getLeaderSocketAddressesFromRM() {
		UDPMessage toSend = new UDPMessage("getAllLeaders", null, true, "");
		UDPMessage.sendMessage(socket, toSend, replicaManagerSocketAddress);
		UDPMessage messageReceived = UDPMessage.receiveUDPMessage(socket);
		if(messageReceived.isSuccess()){
			leaderSocketAddresses = (HashMap<String, InetSocketAddress>) messageReceived.getData();
			logger.log("Fetched leader socket addresses from replica manager: " + leaderSocketAddresses.toString());
		}
		else{
			logger.out("ERROR - " + messageReceived.getErrorMessage());
			System.exit(0);
		}
	}
	/**
	 * Sets up the CORBA service
	 * 
	 * @param string
	 */
	private void setupCORBAService() {

		dpssApiImpl = new DpssApiImpl(socket, leaderSocketAddresses);
		
		String[] corbaArgs = {"orbd", "-ORBInitialPort", "1050", "-ORBInitialHost", "localhost"};
		try {
			ORB orb = ORB.init(corbaArgs, null);
			POA rootpoa = POAHelper.narrow(orb
					.resolve_initial_references("RootPOA"));
			rootpoa.the_POAManager().activate();
			dpssApiImpl.setORB(orb);
			org.omg.CORBA.Object ref = rootpoa
					.servant_to_reference(dpssApiImpl);
			DpssApi href = DpssApiHelper.narrow(ref);
			org.omg.CORBA.Object objRef = orb
					.resolve_initial_references("NameService");
			NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);

			String name = "GameServer_";
			NameComponent path[] = ncRef.to_name(name);
			ncRef.rebind(path, href);
			orb.run();
		} catch (Exception e) {
			System.err.println("ERROR: " + e);
			e.printStackTrace(System.out);
		}
	}

}
